Skip to content

docs(combobox): a11y migration analysis docs - #6618

Open
nikkimk wants to merge 5 commits into
mainfrom
nikkimk/SWC-2453-combobox-a11y
Open

docs(combobox): a11y migration analysis docs#6618
nikkimk wants to merge 5 commits into
mainfrom
nikkimk/SWC-2453-combobox-a11y

Conversation

@nikkimk

@nikkimk nikkimk commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Description

Adds accessibility migration analyses for the 2nd-gen combobox family — a combobox plus the shared listbox and the two option-child components it composes. The combobox and picker share one swc-listbox, and options/option-groups can be children of a listbox, combobox, or picker.

combobox/accessibility-migration-analysis.md

  • Documented ARIA roles/states/properties for swc-combobox — single combobox role on the inner <input>, aria-expanded/aria-controls/aria-activedescendant/aria-autocomplete, keeping aria-selected (chosen value) distinct from the active descendant (keyboard-active row)
  • Documented the composition: a shared swc-listbox rendered in the combobox shadow DOM with swc-option/swc-option-group children projected into it by slot; aria-controls (input→listbox) is same-root, only aria-activedescendant (input→light-DOM option) is cross-root via ariaActiveDescendantElement
  • Flagged an explicit open verification item: that ariaActiveDescendantElement resolves to a slotted option — directly and when nested inside an swc-option-group — across NVDA/JAWS/VoiceOver and Chromium/WebKit/Firefox, with a documented fallback if any combo does not honor slot projection
  • Added a unique-value and distinct-sibling-label rule with a dev warning (value decoupled from label); folded in consumer-reported 1st-gen limitations (key/text separation, popup width/reflow, placeholder) as design motivation
  • Cataloged known 1st-gen issues with dispositions (SWC-1195, SWC-1152, SWC-1127, SWC-2359, SWC-592, SWC-1255); identified the APG editable-combobox-with-list-autocomplete and grouped-listbox patterns

listbox/accessibility-migration-analysis.md (new component swc-listbox)

  • Documented role="listbox" owned on the host via ElementInternals, factored out so combobox and picker share one listbox and it can also be used standalone (APG listbox pattern)
  • Specified the two modes: standalone (focusable, own keyboard model and name) and embedded (rendered in a combobox/picker shadow DOM, not focusable, parent-driven active-descendant); single vs. multi-select (aria-multiselectable, LiveSelectionController); slotted option projection without re-rendering

option/accessibility-migration-analysis.md (new component swc-option)

  • Documented role="option" owned on the host via ElementInternals — enabling the cross-root active-descendant reference and preserving per-option lang (fixes SWC-2359)
  • Distinguished from swc-menu-item (option vs. menuitem); value decoupled from label and unique per widget; aria-disabled over native disabled; distinct-sibling-label rule + dev warning; parent may be a listbox, combobox, or picker

option-group/accessibility-migration-analysis.md (new component swc-option-group)

  • Documented role="group" owned on the host, group naming, and the APG grouped-listbox pattern; sibling-label distinctness scoped within a group; never selectable/focusable/active; no nested groups; flagged as the extra-nesting case for the active-descendant verification

Registered all four docs in 03_components/README.md and regenerated navigation.

Motivation and context

The 2nd-gen migration is an opportunity to address known accessibility gaps, align with the latest WAI-ARIA Authoring Practices, and ensure the components meet WCAG 2.2 AA compliance. Owning role="option"/role="group"/role="listbox" on dedicated components (instead of borrowing sp-menu/sp-menu-item) is what lets the combobox and picker share one listbox and reference real author elements across shadow roots — removing 1st-gen's duplicate-render hack and its defects.

Related issue(s)

  • resolves SWC-2453

Screenshots (if appropriate)


Author's checklist

  • I have read the CONTRIBUTING and PULL_REQUESTS documents.
  • I have reviewed at the Accessibility Practices for this feature, see: Aria Practices
  • I have added automated tests to cover my changes.
  • I have included a well-written changeset if my change needs to be published.
  • I have included updated documentation if my change required it.

Reviewer's checklist

  • Includes a Github Issue with appropriate flag or Jira ticket number without a link
  • Includes thoughtfully written changeset if changes suggested include patch, minor, or major features
  • Automated tests cover all use cases and follow best practices for writing
  • Validated on all supported browsers
  • All VRTs are approved before the author can update Golden Hash

Manual review test cases

@nikkimk nikkimk self-assigned this Aug 13, 2026
@changeset-bot

changeset-bot Bot commented Aug 13, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 18a630a

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@github-actions

Copy link
Copy Markdown
Contributor

📚 Branch Preview Links

🔍 Gen1 Visual Regression Test Results

When a visual regression test fails (or has previously failed while working on this branch), its results can be found in the following URLs:

Deployed to Azure Blob Storage: pr-6618

If the changes are expected, update the current_golden_images_cache hash in the circleci config to accept the new images. Instructions are included in that file.
If the changes are unexpected, you can investigate the cause of the differences and update the code accordingly.

@coveralls

coveralls commented Aug 13, 2026

Copy link
Copy Markdown
Collaborator

Coverage Report for CI Build 31738073455

Warning

No base build found for commit 56fd6ef on main.
Coverage changes can't be calculated without a base build.
If a base build is processing, this comment will update automatically when it completes.

Coverage: 96.241%

Details

  • Patch coverage: No coverable lines changed in this PR.

Uncovered Changes

No uncovered changes found.

Coverage Regressions

Requires a base build to compare against. How to fix this →


Coverage Stats

Coverage Status
Relevant Lines: 39268
Covered Lines: 37993
Line Coverage: 96.75%
Relevant Branches: 6465
Covered Branches: 6021
Branch Coverage: 93.13%
Branches in Coverage %: Yes
Coverage Strength: 460.2 hits per line

💛 - Coveralls

@nikkimk nikkimk added the a11y Issues or PRs related to accessibility label Aug 13, 2026
@nikkimk nikkimk added Status:Ready for review PR ready for review or re-review. High priority PR review PR is a high priority and should be reviewed ASAP labels Aug 13, 2026
@nikkimk
nikkimk marked this pull request as ready for review August 13, 2026 19:53
@nikkimk
nikkimk requested a review from a team as a code owner August 13, 2026 19:53
@pfulton pfulton added the skip_vrt Skip VRT build; mark UI Tests green without running Chromatic label Aug 14, 2026

@5t3ph 5t3ph left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great work pulling all of this together! There does seem to be a key assumption that still needs tested, or if it already has then these docs need updated with that info.

- `aria-activedescendant` (input → active option): **cross-root element reference**. The active option is a light-DOM `swc-option` (projected into the shadow listbox but still rooted in the document), so this uses the ARIA element-reflection property **`ariaActiveDescendantElement`** = `activeOptionElement` (single element; note the singular property name), which resolves across the shadow boundary — the "light DOM siblings via properties" half of the hybrid model in the [web-component form-strategy demos](https://nikkimk.github.io/web-component-form-strategy-demos/demo-hybrid.html).
- Where the listbox or an option needs a name from an element it does not own, use the same element-reference pattern (`ariaLabelledByElements`) rather than an IDREF string.

**Verification required — does `ariaActiveDescendantElement` resolve to a slotted option?** This is the load-bearing assumption of the whole structure and must be proven before the API freezes, not assumed. The reference points from the combobox `<input>` (inside `swc-combobox`'s shadow root) to a `swc-option` that lives in the combobox's light DOM and is *projected by slot* into the shadow `swc-listbox` — and, when grouped, projected through a `swc-option-group` as well. Two things have to hold: (1) the element reference is **valid to set** — referencing outward from a shadow root to an element in a shadow-including ancestor tree is permitted by ARIA element reflection, so this is expected to be allowed; and (2) assistive technology actually **honors** the `aria-activedescendant` when the option is related to the focused input only through `aria-controls` → listbox and slot projection, rather than being a light-DOM descendant of the input. The second point is the risk, because AT resolution of "active descendant" across slot projection — especially with the **extra option-group nesting level** — is not uniformly guaranteed. Test this specific topology (option directly in the combobox, and option inside an `swc-option-group`) in NVDA, JAWS, and VoiceOver across Chromium, WebKit, and Firefox before relying on it; if an AT/browser combination does not follow the projection, fall back for that case (for example an `aria-owns` relationship establishing the listbox→option ownership in the flattened tree, or a documented limitation) rather than silently shipping a broken reference. Capture the result in this doc when known.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is pretty important verification since it's assumptions that listbox and option depend on, especially since swc-option explicitly states it exists for role ownership . Do you think it's worth a spike/extra demo to verify this ahead of time, including the nested case?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

a11y Issues or PRs related to accessibility High priority PR review PR is a high priority and should be reviewed ASAP skip_vrt Skip VRT build; mark UI Tests green without running Chromatic Status:Ready for review PR ready for review or re-review.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants